function isString(obj) {
  return typeof obj === "string";
}

/**
 * 下载资源
 * @param {String} url 资源路径
 * @param {String} fileName 文件名
 */
function aDownload(url, fileName) {
  // html5 A[download]
  const anchor = document.createElement("a");
  anchor.href = url;
  anchor.setAttribute("download", fileName);
  anchor.style.display = "none";
  document.body.appendChild(anchor);
  setTimeout(function () {
    anchor.click();
    document.body.removeChild(anchor);
    setTimeout(function () {
      self.URL.revokeObjectURL(anchor.href);
    }, 250);
  }, 0);
}

function strToBlob(content, type) {
  return new Blob([content], { type });
}

/**
 * 下载文本
 * @param {string} str 文本
 * @param {string} fileName 文件名
 * @param {string} type 文件MIME类型
 */
function downloadString(str, fileName, type = "") {
  const src = URL.createObjectURL(strToBlob(str, type));
  aDownload(src, fileName);
}

function downloadJson(content, fileName) {
  const type = "application/json";
  downloadString(
    isString(content) ? content : JSON.stringify(content, null, 2),
    fileName + ".json",
    type
  );
}

function downloadSvg(content, fileName) {
  const type = "image/svg+xml";
  downloadString(content, fileName + ".svg", type);
}

/**
 * 获取网站的MD引用
 * @returns 网站的MD引用
 */
function getReference() {
  return `[${document.title}](${window.location.href})`;
}

/**
 * 复制到剪贴板
 * @param {String} value
 */
function copy2Clipboard(value) {
  const input = document.createElement("input");
  document.body.appendChild(input);
  input.setAttribute("value", value);
  input.select();
  if (document.execCommand("copy")) {
    document.execCommand("copy");
  }
  document.body.removeChild(input);
}

/**
 * canvas 对象
 */
function createCanvas(width = 300, height = 150) {
  const canvas = document.createElement("canvas");
  canvas.width = width;
  canvas.height = height;
  return canvas;
}

/**
 * 根据 src 获取图片高度
 * @param {string} src dataURL
 * @returns {Promise<[width, height]>}
 */
function getImageHeight(src) {
  return new Promise((res, rej) => {
    const img = new Image();
    img.src = src;
    img.onload = function () {
      res([this.width, this.height]);
    };
    img.onerror = function (e) {
      rej(e);
    };
  });
}

/**
 * 读取文件内容
 * @param {File} file
 * @param {string} encode
 * @returns string
 */
async function readFileAsText(file, encode) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = function () {
      resolve(reader.result);
    };
    reader.onerror = function () {
      reject(reader.result);
    };
    reader.readAsText(file, encode);
  });
}

/**
 * 图片转换为DataURL
 * @param {File} img
 */
async function readImageAsDataURL(img) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = function () {
      resolve(reader.result);
    };
    reader.onerror = function () {
      reject(reader.error);
    };
    reader.readAsDataURL(img);
  });
}

/**
 * 图片转换为DataURL(回调格式)
 * @param {File} img
 * @param {Function} cb
 */
function parseImageAsDataURL(img, cb) {
  const reader = new FileReader();
  reader.onload = function () {
    cb(reader.result);
  };
  reader.readAsDataURL(img);
}

/**
 * 防抖
 * @param {Function} func
 * @param {Number} delay
 * @returns {Function}
 */
function debounce(func, delay) {
  let timer = null;
  return function (...args) {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
    timer = setTimeout(() => {
      func(...args);
      timer = null;
    }, delay);
  };
}

/**
 * 节流
 * @param {Function} func
 * @param {Number} delay
 * @returns {Function}
 */
function throttle(func, delay) {
  let timer = null;
  let fresh = false;
  let lastCallArgs = [];
  const funcProxy = function (...args) {
    if (!timer) {
      fresh = false;
      timer = setTimeout(() => {
        func(...args);
        timer = null;
        if (fresh) {
          funcProxy(...lastCallArgs);
        }
      }, delay);
    } else {
      fresh = true;
      lastCallArgs = args;
    }
  };
  return funcProxy;
}

/**
 * 监听 dom size变化时执行 func
 * @param {Function} func 回调
 * @param {Element} dom 元素
 * @returns 取消观察的方法
 */
function whenResize(func, dom, delay = 750) {
  const _func = debounce(func, delay);
  let prev = [0, 0];
  const resizeObserver = new ResizeObserver((entries) => {
    for (const entry of entries) {
      if (
        prev[0] !== entry.target.clientWidth ||
        prev[1] !== entry.target.clientHeight
      ) {
        _func([entry.target.clientWidth, entry.target.clientHeight]);
        prev = [entry.target.clientWidth, entry.target.clientHeight];
      }
    }
  });
  resizeObserver.observe(dom);

  return () => resizeObserver.unobserve(dom);
}

/**
 * 灰度化
 * @param {ImageData} imageData
 * @returns {ImageData}
 */
function grayImageData(imageData) {
  const { data, width, height } = imageData;
  const _data = new Uint8ClampedArray(height * width * 4);
  for (let i = 0; i < data.length; i += 4) {
    const [Red, Green, Blue, Alpha] = data.slice(i, i + 4);
    const gray = Red * 0.3 + Green * 0.59 + Blue * 0.11;
    _data[i] = gray;
    _data[i + 1] = gray;
    _data[i + 2] = gray;
    _data[i + 3] = Alpha;
  }

  return new ImageData(_data, width, height);
}

/**
 * 图片降维: 四位归一灰度化
 * @param {ImageData} imageData
 * @returns - { data, width, height }
 */
function reduceImageData(imageData) {
  const { data, width, height } = imageData;
  const _data = new Uint8ClampedArray(height * width);
  for (let i = 0; i < data.length; i += 4) {
    const [Red, Green, Blue] = data.slice(i, i + 4);
    const gray = Red * 0.3 + Green * 0.59 + Blue * 0.11;
    _data[(i / 4) >>> 0] = gray;
  }

  return { data: _data, width, height };
}

export { aDownload, copy2Clipboard, createCanvas, debounce, downloadJson, downloadString, downloadSvg, getImageHeight, getReference, grayImageData, parseImageAsDataURL, readFileAsText, readImageAsDataURL, reduceImageData, strToBlob, throttle, whenResize };
